package bingosoft.metro.datasync.base.service;

import bingosoft.components.base.service.BaseClass;
import bingosoft.components.base.service.Result;
import bingosoft.metro.datasync.base.iservice.ISyncService;
import bingosoft.metro.datasync.base.model.SyncLogModel;
import bingosoft.metro.datasync.base.model.SyncModel;
import leap.core.AppContext;
import leap.db.Db;
import leap.lang.Strings;
import leap.orm.dao.Dao;
import leap.orm.model.Model;
import leap.orm.sql.SqlCommand;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Feily
 * @功能描述 全量同步数据服务基础类
 * @创建时间 2017-12-13
 */
public abstract class SyncService extends BaseClass implements ISyncService {

	protected Dao dao;

	/**
	 * 删除同步数据临时表SQL
	 */
	protected String deleteSyncDataSql;
	/**
	 * 根据同步数据保存目标数据SQL
	 */
	protected String saveTargetDataSql;
	/**
	 * 根据同步数据修改目标数据SQL
	 */
	protected String updateTargetDataSql;
	/**
	 * 根据同步数据删除目标表中的已删除数据SQL
	 */
	protected String deleteTargetDataSql;
	/**
	 * 租户编码
	 */
	protected String tenantCode;
	/**
	 * 获取最后更新时间SQL
	 */
	protected String getLastSyncTimeSql;
	/**
	 * 是否全量更新
	 */
	protected boolean isFullSync;
	/**
	 * 同步类型
	 */
	protected String syncType;

	//是否物理删除
	protected boolean physicallyDelete = AppContext.config().getBooleanProperty("sync.physicallyDelete", false);

	//是否使用自定义sql语句
	protected boolean useCustomSql = AppContext.config().getBooleanProperty("sync.useCustomSql", false);

	//获取最后更新时间sql模板
	protected String getLastUpdateTimeSqlTmpl = getSqlTemplate("sqlTmpl.getLastUpdateTime");

	//保存目标表数据sql模板
	protected String saveTargetDataSqlTmpl = getSqlTemplate("sqlTmpl.saveTargetData");

	//修改目标表数据sql模板
	protected String updateTargetDataSqlTmpl = getSqlTemplate("sqlTmpl.updateTargetData");

	//删除目标表中的已删除数据（主表使用）
	protected String deleteTargetDataSqlTmpl = getSqlTemplate("sqlTmpl.deleteTargetData");

	//删除目标表数据sql模板（关联表使用）
	protected String fullDeleteTargetDataSqlTmpl = getSqlTemplate("sqlTmpl.fullDeleteTargetData");

	//全量保存目标表数据sql模板（关联表使用）
	protected String fullSaveTargetDataqlTmpl = getSqlTemplate("sqlTmpl.fullSaveTargetData");

	//同步列配置文档路径
	protected String syncConfigDocPath;

	//同步列配置文档对象
	protected Document syncConfigDoc;

	public String getTenantCode() {
		return tenantCode;
	}

	public void setTenantCode(String tenantCode) {
		this.tenantCode = tenantCode;
	}

	public boolean isFullSync() {
		return isFullSync;
	}

	public void setFullSync(boolean fullSync) {
		isFullSync = fullSync;
	}

	@Override
	public Result syncData() {
		this.logMethodCalled("syncData");
		
		Result result = new Result();
		String message = "成功同步数据{0}条";

		//创建日志对象，记录开始时间，设置同步状态
		SyncLogModel syncLogModel = new SyncLogModel();
		syncLogModel.setBeginTime(new Date());
		syncLogModel.setIsSuccess(1);
		
		try {
			//获取同步数据信息列表
			List<Model> syncData = this.getSyncDataList();
			if(syncData == null || syncData.size() == 0){
				message = Strings.format(message, 0);
				this.logMessage(message);
				result.setMessage(message);
				return result;
			}
			//删除历史同步数据
			this.deleteSyncData();

			//保存同步数据
			this.saveSyncData(syncData);

			//根据同步数据保存或修改目标数据
			if(AppContext.config().getBooleanProperty("sync.syncTargetData", true)){
				this.syncTargetData();
			}

			//设置同步数量
			syncLogModel.setSyncCount(syncData.size());

			message = Strings.format(message, syncData.size());
			this.logMessage(message);
			result.setMessage(message);
			return result;
		} catch (Exception ex) {
			this.logError("同步数据出错", ex);

			//设置同步状态，记录错误信息
			syncLogModel.setIsSuccess(0);
			syncLogModel.setError(ex.getMessage());

			result.setSuccess(false);
			result.setMessage(ex.getMessage());
			return result;
		} finally {
			//新增同步日志
			syncLogModel.setEndTime(new Date());
			syncLogModel.setSyncType(this.syncType);
			syncLogModel.create();
		}
	}

	public List<Model> getSyncDataList() {
		// 获取最后更新时间
		Date lastSyncTime;
		if(isFullSync){
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
			try {
				lastSyncTime = simpleDateFormat.parse("1900-01-01");
			} catch (ParseException e) {
				throw new RuntimeException("获取最后更新时间失败【日期转换失败】");
			}
		}else{
			lastSyncTime = this.getLastSyncTime();
		}
		// 获取同步数据信息列表
		List<Model> syncData = this.getSyncDataList(lastSyncTime);
		return syncData;
	}

	/**
	 * 获取最后更新时间
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public Date getLastSyncTime() {
		this.logMethodCalled("getLastSyncTime");

		if(Strings.isEmpty(this.getLastSyncTimeSql)){
			throw new RuntimeException("属性【getLastSyncTimeSql】不能为空");
		}

		Map<String, Object> params = new HashMap<String, Object>();
		params.put("tenantCode", this.tenantCode);

		SyncModel syncModel = SyncModel.<SyncModel>query(this.getLastSyncTimeSql).params(params).firstOrNull();
		if(syncModel != null && syncModel.getLastSyncTime() != null){
			return syncModel.getLastSyncTime();
		}else{
			int year = 10;
			this.logMessage(Strings.format("最后更新时间为空，默认使用{0}年前时间", year));

			Date date = new Date();
			date.setYear(date.getYear() - year);
			return date;
		}
	}

	/**
	 * 获取同步数据信息列表
	 * @param lastSyncTime
	 * @return
	 */
	public abstract List<Model> getSyncDataList(Date lastSyncTime);

	@Override
	public Result deleteSyncData() {
		this.logMethodCalled("deleteSyncData");
	
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("tenantCode", this.tenantCode);
		
		getDao().executeUpdate(this.deleteSyncDataSql, params);
		
		return new Result();
	}

	@Override
	public Result saveSyncData(List<Model> syncData) {
		this.logMethodCalled("saveSyncData");
		
		getDao().batchInsert(syncData);
		
		return new Result();
	}

	@Override
	public Result syncTargetData() {
		this.logMethodCalled("saveOrUpdateTargetData");
	
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("tenantCode", this.tenantCode);

		// 删除目标表中的已删除数据
		if(this.physicallyDelete){
			getDao().executeUpdate(this.deleteTargetDataSql, params);
		}

		// 修改目标数据
		getDao().executeUpdate(this.updateTargetDataSql, params);
		
		// 保存目标数据
		getDao().executeUpdate(this.saveTargetDataSql, params);

		return new Result();
	}

	/**
	 * 获取最后更新时间SQL
	 * @return
	 */
	public String buildGetLastUpdateTimeSql() throws DocumentException {
		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//目标表名
		String targetTable = table.getText();
		//更新时间列配置元素
		String updateTimeColumn = config.elementText("updateTimeColumn");
		//sql模板
		String sql = this.getLastUpdateTimeSqlTmpl;

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#updateTimeColumn#", updateTimeColumn);

		return sql;
	}

	/**
	 * 根据同步数据保存组织数据SQL
	 * @return
	 * @throws DocumentException
	 */
	public String buildSaveTargetDataSql() throws DocumentException {

		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//主键配置元素
		Element key = config.element("key");
		//列配置元素
		Element columns = config.element("columns");
		//目标表名
		String targetTable = table.getText();
		//源表名
		String sourceTable = table.attributeValue("sourceTable");
		//目标主键
		String targetKey = key.getText();
		//源主键
		String sourceKey = key.attributeValue("sourceColumn");
		//sql模板
		String sql = this.saveTargetDataSqlTmpl;
		//目标列
		String targetColumns = "";
		//源列
		String sourceColumns = "";

		//循环读取列配置
		for (Iterator iter = columns.elementIterator("column"); iter.hasNext();) {
			Element ele = (Element) iter.next();
			String sourceName = ele.attribute("sourceColumn").getText();
			String targetName = ele.getText();
			if(Strings.isEmpty(sourceName) || Strings.isEmpty(targetName)){
				continue;
			}

			targetColumns += Strings.format(", {0}", targetName);
			sourceColumns += Strings.format(", {0}", sourceName);

		}

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#sourceTable#", sourceTable)
				.replaceAll("#targetKey#", targetKey)
				.replaceAll("#sourceKey#", sourceKey)
				.replaceAll("#targetColumns#", targetColumns.substring(1))
				.replaceAll("#sourceColumns#", sourceColumns.substring(1));

		return sql;
	}

	/**
	 * 根据同步数据修改组织数据SQL
	 * @return
	 * @throws DocumentException
	 */
	public String buildUpdateTargetDataSql() throws DocumentException {

		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//主键配置元素
		Element key = config.element("key");
		//列配置元素
		Element columns = config.element("columns");
		//目标表名
		String targetTable = table.getText();
		//源表名
		String sourceTable = table.attributeValue("sourceTable");
		//目标主键
		String targetKey = key.getText();
		//源主键
		String sourceKey = key.attributeValue("sourceColumn");
		//sql模板
		String sql = this.updateTargetDataSqlTmpl;
		//修改列sql
		String updateColumns = "";

		//循环读取配置
		for (Iterator iter = columns.elementIterator("column"); iter.hasNext();) {
			Element ele = (Element) iter.next();
			String sourceName = ele.attribute("sourceColumn").getText();
			String targetName = ele.getText();
			if(Strings.isEmpty(sourceName) || Strings.isEmpty(targetName) || targetName.equals(targetKey)){
				continue;
			}

			updateColumns += Strings.format(", t.{0} = s.{1}", targetName, sourceName);

		}

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#sourceTable#", sourceTable)
				.replaceAll("#targetKey#", targetKey)
				.replaceAll("#sourceKey#", sourceKey)
				.replaceAll("#updateColumns#", updateColumns.substring(1));

		return sql;
	}

	/**
	 * 删除目标表中的已删除数据SQL
	 * @return
	 * @throws DocumentException
	 */
	public String buildDeleteTargetDataSql() throws DocumentException {

		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//主键配置元素
		Element key = config.element("key");
		//目标表名
		String targetTable = table.getText();
		//源表名
		String sourceTable = table.attributeValue("sourceTable");
		//目标主键
		String targetKey = key.getText();
		//源主键
		String sourceKey = key.attributeValue("sourceColumn");
		//sql模板
		String sql = this.deleteTargetDataSqlTmpl;

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#sourceTable#", sourceTable)
				.replaceAll("#targetKey#", targetKey)
				.replaceAll("#sourceKey#", sourceKey);

		return sql;
	}


	/**
	 * 保存同步日志
	 * @return
	 */
	protected Result saveSyncLog(SyncLogModel syncLogModel){

		Result result = new Result();

		return result;
	}

	/**
	 * 获取Dao对象
	 * @return
	 */
	protected Dao getDao() {
		if(dao == null){
			dao = Dao.get();
		}
		return dao;
	}

	/**
	 * 获取数据库对象
	 * @return
	 */
	protected Db getDb() {
		return getDao().getOrmContext().getDb();
	}

	/**
	 * 获取附加数据库版本的Sql内容
	 * @param sqlKey
	 * @return
	 */
	protected String getSqlContent(String sqlKey) {
		String dbName = "";
		if(getDb().isPostgreSQL()){
			dbName = "postgreSql";
		}else if(getDb().isOracle()){
			dbName = "oracle";
		}else if(getDb().isMySql()){
			dbName = "mySql";
		}else if(getDb().isSqlServer()){
			dbName = "sqlServer";
		}

		String newSqlKey = "";
		if(!Strings.isEmpty(dbName)){
			newSqlKey = sqlKey + "$" + dbName;
		}

		SqlCommand sqlCommand = Dao.get().getOrmContext().getMetadata().tryGetSqlCommand(newSqlKey);
		if(sqlCommand == null){
			// 如果数据库重载版本不存在，则使用默认SQL
			sqlCommand = Dao.get().getOrmContext().getMetadata().tryGetSqlCommand(sqlKey);
		}

		if(sqlCommand == null){
			String message = Strings.format("参数【sqlKey】的值（{0}）不存在", sqlKey);
			throw new RuntimeException(message);
		}
		return sqlCommand.getSql();
	}

	/**
	 * 获取sql模板
	 * @param propertyName
	 * @return
	 */
	protected String getSqlTemplate(String propertyName) {
		String dbName = "";
		if(getDb().isPostgreSQL()){
			dbName = "postgreSql";
		}else if(getDb().isOracle()){
			dbName = "oracle";
		}else if(getDb().isMySql()){
			dbName = "mySql";
		}else if(getDb().isSqlServer()){
			dbName = "sqlServer";
		}

		String newPropertyName = "";
		if(!Strings.isEmpty(dbName)){
			newPropertyName = propertyName + "$" + dbName;
		}

		String sqlTemplate = AppContext.config().getProperty(newPropertyName);
		if(sqlTemplate == null){
			// 如果数据库重载版本不存在，则使用默认SQL
			sqlTemplate = AppContext.config().getProperty(propertyName);
		}

		if(sqlTemplate == null){
			String message = Strings.format("sql模板（{0}）不存在", propertyName);
			throw new RuntimeException(message);
		}
		return sqlTemplate;
	}

	/**
	 * 获取同步配置文档对象
	 * @return
	 * @throws DocumentException
	 */
	protected Document getSyncConfigDoc() throws DocumentException {
		if (syncConfigDoc == null){
			//获取配置文件资源
			SAXReader read = new SAXReader();
			syncConfigDoc = read.read(this.getClass().getResourceAsStream(syncConfigDocPath));
		}
		return syncConfigDoc;
	}
}